KMS 暗号化が有効な SSM セッションマネージャー接続を MFA + スイッチロール + AWS CLI で実行してエラーが発生した時の回避方法
コンバンハ、千葉(幸)です。
今回の事象が発生した構成は以下の通りです。
- AWSアカウント
- スイッチ元:
000000000000
- スイッチ先:
999999999999
- スイッチ元:
- スイッチ先アカウントの EC2 インスタンスに対して AWS CLI でセッションマネージャー接続を行う
- セッションマネージャー接続は KMS による暗号化が有効になっている
- スイッチ時には MFA による認証コードの入力を行う
- AWS CLI によるスイッチロールはプロファイルを指定して実行する
実行を試みると以下のエラーが発生しました。
% aws ssm start-session --target i-0938992d87f175f68 --profile chiba Enter MFA code for arn:aws:iam::000000000000:mfa/cm-chiba.yukihiro: Starting session with SessionId: botocore-session-1616934847-07ebf73805201d7cf SessionId: botocore-session-1616934847-07ebf73805201d7cf : ----------ERROR------- Encountered error while initiating handshake. KMSEncryption failed on client with status 2 error: Failed to process action KMSEncryption: error while creating new KMS service, Error creating new aws sdk session AssumeRoleTokenProviderNotSetError: assume role with MFA enabled, but AssumeRoleTokenProvider session option not set.
特に気になるのは以下の部分です。
Error creating new aws sdk session AssumeRoleTokenProviderNotSetError: assume role with MFA enabled, but AssumeRoleTokenProvider session option not set.
マネジメントコンソールからの接続や MFA なしでの AWS CLI 実行は問題なく行えたのですが、 MFA が絡んだときにうまく行かなくなる、という状況でした。
回避策とあわせてご紹介します。
回避策の概要
以下の 3 パターンを取り上げます。
- KMS 暗号化を無効化する
- AssumeRole を事前に実行する
- 外部ツールを使用する
事象について改めて整理
セッションマネージャーの KMS 暗号化
セッションマネージャー接続設定のオプションとして、KMS による暗号化を有効化できます。これにより「接続先の EC2 インスタンスと接続元のローカル端末間のセッションデータ」が KMS カスタマーマスターキーを使用して暗号化されます。(有効化前のデフォルト状態では TLS 1.2 暗号化のみがなされています。)
コンソールでは以下箇所から状態が確認できます。当該画面には映っていませんが、編集画面では特定の KMS キーを指定することになります。
こういったセッションマネージャーに関する設定内容は SSM ドキュメント SSM-SessionManagerRunShell に反映されます。セッションマネージャー実行時にはこのドキュメントが呼び出されます。
パラメータとしてkmsKeyId
という項目が含まれていますね。
KMS による暗号化を有効化すると、以下のエンティティで KMS キーに関するアクセス許可が必要となります。
- セッションマネージャーを開始する IAM エンティティ(今回はスイッチ後のロール)
kms:GenerateDataKey
- 接続先 EC2 インスタンスにアタッチされている IAM ロール
kms:Decrypt
Enable AWS KMS key encryption of session data (console) - AWS Systems Manager
各リソースのポリシー設定
冒頭のエラーが発生した際の権限設定は以下の通りでした。スイッチ元のユーザーの権限は特に関係しないので省略します。
- スイッチ先のロール
- AWS 管理ポリシー
ReadOnlyAccess
- カスタムポリシー(後述)
- AWS 管理ポリシー
- 接続先インスタンスのロール
- AWS 管理ポリシー
AmazonSSM ManagedInstanceCore
- AWS 管理ポリシー
- KMS キーポリシー
- 上記二つの IAM ロールをキーユーザーとして指定(後述)
スイッチ先ロールのカスタムポリシー
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "ssm:ResumeSession", "ssm:TerminateSession", "ssm:StartSession" ], "Resource": [ "arn:aws:ec2:*:999999999999:instance/*", "arn:aws:ssm:*:999999999999:session/*", "arn:aws:ssm:*:999999999999:document/*" ], "Effect": "Allow" } ] }
KMS キーポリシー
コンソールから「キーユーザー」として二つのロールを選択した際に自動で生成されたものです。
なお、ロール名はそれぞれ以下の通りです。
- スイッチ先のロール:
Role-for-switch
- 接続先インスタンスのロール:
AmazonSSMRoleForInstancesQuickSetup
{ "Id": "key-consolepolicy-3", "Version": "2012-10-17", "Statement": [ { "Sid": "Enable IAM User Permissions", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::999999999999:root" }, "Action": "kms:*", "Resource": "*" }, { "Sid": "Allow use of the key", "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::999999999999:role/AmazonSSMRoleForInstancesQuickSetup", "arn:aws:iam::999999999999:role/Role-for-switch" ] }, "Action": [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:DescribeKey" ], "Resource": "*" }, { "Sid": "Allow attachment of persistent resources", "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::999999999999:role/AmazonSSMRoleForInstancesQuickSetup", "arn:aws:iam::999999999999:role/Role-for-switch" ] }, "Action": [ "kms:CreateGrant", "kms:ListGrants", "kms:RevokeGrant" ], "Resource": "*", "Condition": { "Bool": { "kms:GrantIsForAWSResource": "true" } } } ] }
スイッチ先ロール、インスタンス用ロールのアイデンティティベースポリシーでは KMS に関するアクションが Allow されていませんが、キーポリシー側で許可されているためにアクションが可能、という状態です。
マネジメントコンソールでの接続
上記の権限構成で、マネジメントコンソールから接続を試みます。問題なく接続できます。
This session is encrypted using AWS KMS.
のメッセージとともに、正常に接続できました。
AWS CLI (MFA なし)での接続
事前に Session Manager plugin のインストールを済ませています。
また、コンフィグファイルの設定状況は以下の通りです。
[default] region = ap-northeast-1 [profile chiba] region = ap-northeast-1 role_arn = arn:aws:iam::999999999999:role/Role-for-switch source_profile = cm-base # mfa_serial = arn:aws:iam::000000000000:mfa/cm-chiba.yukihiro
ここではmfa_serial
はコメントアウトしています。
[default] aws_access_key_id = AKIAQ3BIIHXXXXXXXXXX aws_secret_access_key = XXXXXXXXXX0gHnBTNsj5b [cm-base] aws_access_key_id = AKIARK998PXXXXXXXXXX aws_secret_access_key = XXXXXXXXXXYqVryTVSURj
プロファイルchiba
では、source_profile
としてcm-base
を指定しています。
上記の状態で名前つきプロファイルを指定してaws ssm start-session
を実行すると、問題なく接続が成功します。
% aws ssm start-session --target i-0938992d87f175f68 --profile chiba Starting session with SessionId: botocore-session-1616934777-0b94b704a86b058f0 This session is encrypted using AWS KMS. sh-4.2$
AWS CLI (MFAあり)での接続
先ほどのコンフィグから mfa_serial
をアンコメントし、スイッチ時の MFA を有効化します。
[profile chiba] region = ap-northeast-1 role_arn = arn:aws:iam::999999999999:role/Role-for-switch source_profile = cm-base mfa_serial = arn:aws:iam::000000000000:mfa/cm-chiba.yukihiro
再びプロファイルを指定してaws ssm start-session
を実行し、MFA コードを入力します。冒頭で載せたエラーが発生します。
% aws ssm start-session --target i-0938992d87f175f68 --profile chiba Enter MFA code for arn:aws:iam::000000000000:mfa/cm-chiba.yukihiro: Starting session with SessionId: botocore-session-1616934847-021099c96056c99dd SessionId: botocore-session-1616934847-021099c96056c99dd : ----------ERROR------- Encountered error while initiating handshake. KMSEncryption failed on client with status 2 error: Failed to process action KMSEncryption: error while creating new KMS service, Error creating new aws sdk session AssumeRoleTokenProviderNotSetError: assume role with MFA enabled, but AssumeRoleTokenProvider session option not set.
関連する権限構成は同一であっても、AWS CLI で MFA を使用する場合のみエラーが発生する、という事象に直面しました。
ちなみに
この事象は権限設定に起因したものではありません。念のためにスイッチ先ロールと接続先インスタンスのロールにAdministratorAccess
をアタッチして試行しても同様の結果となりました。
どのように回避すればいいのか
今回は以下のパターンをご紹介します。
- KMS 暗号化を無効化する
- AssumeRole を事前に実行する
- 外部ツールを使用する
厳密に考えるとパターン 2 と 3 は同じ括りなのですが、アプローチが異なるので別物としました。
KMS 暗号化を無効化する
暗号化を無効化することで、MFA ありでも問題なく接続できるようになります。「 KMS による暗号化を必須とする」といった社内のセキュリティポリシーが定められていない場合には、このアプローチが最もシンプルに事象を回避できます。
KMS 暗号化を無効化した際の分かりやすい影響としては、「制御できるレイヤーが一つ減る」というものがあります。
再掲となりますが、KMS 暗号化が有効な場合には以下の権限設定が必要となります。
接続を開始する IAM エンティティに KMS に対する権限が足りない場合には以下のエラーが発生します。
% aws ssm start-session --target i-0938992d87f175f68 --profile chiba Starting session with SessionId: botocore-session-1616930143-05560414b3eb7b9ef SessionId: botocore-session-1616930143-05560414b3eb7b9ef : ----------ERROR------- Encountered error while initiating handshake. KMSEncryption failed on client with status 2 error: Failed to process action KMSEncryption: Error calling KMS GenerateDataKey API: AccessDeniedException: User: arn:aws:sts::999999999999:assumed-role/Role-for-switch/1616930144774813000 is not authorized to perform: kms:GenerateDataKey on resource: arn:aws:kms:ap-northeast-1:999999999999:key/XXXXXXXX-8d89-4c70-8b93-108ca3283f27 status code: 400, request id: effaa166-de06-4f2c-a582-b389518142a0
EC2 インスタンス用ロールの権限が足りない場合は以下のメッセージが表示されます。
% aws ssm start-session --target i-0938992d87f175f68 --profile chiba Starting session with SessionId: botocore-session-1616930143-05560414b3eb7b9ef SessionId: botocore-session-1616930143-05560414b3eb7b9ef : ----------ERROR------- Encountered error while initiating handshake. Fetching data key failed: Unable to retrieve data key, Error when decrypting data key AccessDeniedException: The ciphertext refers to a customer master key that does not exist, does not exist in this region, or you are not allowed to access. status code: 400, request id: 1c93bcd2-57ec-4bf4-beee-0e1821d50bf2
セッションマネージャー接続そのものに対する権限とは別に、 KMS に対する権限が必要となります。こういったコントロールが必須ではない場合には、暗号化を無効化するのも一つの手です。
AssumeRole を事前に実行する
エラーが発生した際は、CLI 実行時のオプションとしてプロファイルを指定することで、role_arn
で指定したロールへの自動的な AssumeRole を実現していました。
[default] region = ap-northeast-1 [profile chiba] region = ap-northeast-1 role_arn = arn:aws:iam::999999999999:role/Role-for-switch source_profile = cm-base mfa_serial = arn:aws:iam::000000000000:mfa/cm-chiba.yukihiro
以下のように事前に明示的に AssumeRole を行う方法を取ることで、事象を回避できました。なお、ここでは default のプロファイルを使用しています。
% TOKENCODE=000000 aws_credentials=$(aws sts assume-role --role-arn arn:aws:iam::999999999999:role/Role-for-switch --role-session-name RoleSession1 --serial-number arn:aws:iam::999999999999:mfa/chiba-cli --token-code $TOKENCODE) export AWS_ACCESS_KEY_ID=$(echo $aws_credentials|jq -r '.Credentials.AccessKeyId') export AWS_SECRET_ACCESS_KEY=$(echo $aws_credentials|jq -r '.Credentials.SecretAccessKey') export AWS_SESSION_TOKEN=$(echo $aws_credentials|jq -r '.Credentials.SessionToken')
( assume-role 時に指定しているオプションは以下の通りです。)
aws sts assume-role\ --role-arn arn:aws:iam::999999999999:role/Role-for-switch\ --role-session-name RoleSession1\ --serial-number arn:aws:iam::999999999999:mfa/chiba-cli\ --token-code $TOKENCODE
AssumeRole 後に念のため IAM エンティティを確認すると、ロールを引き受けた状態であることが確認できます。
% aws sts get-caller-identity { "UserId": "AROAQ3BIIH736USBVHHYC:RoleSession1", "Account": "999999999999", "Arn": "arn:aws:sts::999999999999:assumed-role/Role-for-switch/RoleSession1" }
この状態で aws ssm start-session を実行すると、 KMS で暗号化された状態でのセッションマネージャー接続を開始できました。
% aws ssm start-session --target i-01456ff79ca9f6247 Starting session with SessionId: RoleSession1-0a171025a0417765b This session is encrypted using AWS KMS. sh-4.2$
assume-role 部分をある程度自動化したい、となると以下のようにスクリプトを作成するのもよいでしょう。
名前つきプロファイルについてちょっと勘違い
以下のような .aws/config
にした上でプロファイルを指定(--profile chiba
)して AssumeRole をしようとすると失敗しました。
[default] region = ap-northeast-1 [profile chiba] region = ap-northeast-1 # role_arn = arn:aws:iam::999999999999:role/Role-for-switch source_profile = cm-base # mfa_serial = arn:aws:iam::000000000000:mfa/cm-chiba.yukihiro
% TOKENCODE=000000 aws sts assume-role\ --role-arn arn:aws:iam::999999999999:role/Role-for-switch\ --role-session-name RoleSession1\ --serial-number arn:aws:iam::999999999999:mfa/chiba-cli\ --token-code $TOKENCODE\ --profile chiba Unable to locate credentials. You can configure credentials by running "aws configure".
credentials が見つからない、という怒られのようです。source_profile
で指定しているのに何故……?と疑問に思いました。
調べ直したところ、source_profile
はrole_arn
とセットで使用するものであり、単独で.aws/credentials
内のエントリを指定できるものではありませんでした。
今回使用したいプロファイル名chiba
と同名のエントリを.aws/credentials
に追加することで、エラーは回避できました。
[default] aws_access_key_id = AKIAQ3BIIHXXXXXXXXXX aws_secret_access_key = XXXXXXXXXX0gHnBTNsj5b #[cm-base] #aws_access_key_id = AKIARK998PXXXXXXXXXX #aws_secret_access_key = XXXXXXXXXXYqVryTVSURj [chiba] aws_access_key_id = AKIARK998PXXXXXXXXXX aws_secret_access_key = XXXXXXXXXXYqVryTVSURj
外部ツールを使用する
上記のパターンで行った AssumeRole の操作をなるべくラップしたい、という場合には外部ツールを使用するのも一つの手です。
AssumeRole 補助ツールはいくつかありますが、今回は以下エントリで取り上げられている remind101/assume-role を使用してみました。
筆者の環境は mac のため、以下コマンドでインストールを行います。
$ brew install remind101/formulae/assume-role
設定ファイルはパターン 2 と変わらず以下の通りです。
[default] region = ap-northeast-1 [profile chiba] region = ap-northeast-1 role_arn = arn:aws:iam::999999999999:role/Role-for-switch source_profile = cm-base mfa_serial = arn:aws:iam::000000000000:mfa/cm-chiba.yukihiro
[default] aws_access_key_id = AKIAQ3BIIHXXXXXXXXXX aws_secret_access_key = XXXXXXXXXX0gHnBTNsj5b [cm-base] aws_access_key_id = AKIARK998PXXXXXXXXXX aws_secret_access_key = XXXXXXXXXXYqVryTVSURj
assume-role <プロファイル名>
の後に、実行したい aws cli コマンドを続けます。MFA コードの入力が求められたのち、コマンドが実行されます。
% assume-role chiba aws ssm start-session --target i-01456ff79ca9f6247 MFA code: 000000 Starting session with SessionId: 1617330888779191000-0cabd3eb74fa1fb2f This session is encrypted using AWS KMS. sh-4.2$
特に AssumeRole の処理を意識することなく、プロファイルの指定だけで事象を回避できました。
終わりに
KMS による暗号化が有効な SSM セッションマネージャー接続を、 MFA コード入力を伴う AWS CLI (プロファイル指定でのスイッチロールパターン)で実行しようとした際に発生するエラーについてでした。
AWS CLI でのスイッチロールはプロファイルで指定しておくのが便利ですが、組み合わせによっては MFA コードの受け渡し(という表現が正しいのか分かりませんが)がうまく行かないことがあるようです。
(Error creating new aws sdk session AssumeRoleTokenProviderNotSetError: assume role with MFA enabled, but AssumeRoleTokenProvider session option not set.
で検索すると、以下のような Terraform 関連が多くヒットしました。他にもありそうだなーと思っています。)
環境によっては外部ツールの使用が認められていないこともあるかと思いますので、その場合にはパターン 1 や 2 で回避いただければと思います。
以上、千葉(幸)がお送りしました。